iT邦幫忙

2025 iThome 鐵人賽

DAY 21
1

主題

在我們平常玩的踩地雷(Minesweeper)遊戲裡,最常見的功能之一就是「選擇難度」。
一般來說會有三種經典模式:

  • 初級 (Beginner):9x9 棋盤,10 顆地雷
  • 中級 (Intermediate):16x16 棋盤,40 顆地雷
  • 高級 (Expert):30x16 棋盤,99 顆地雷

這個功能看似簡單,但在實作時有不少設計上的小細節值得思考。今天就來分享一下我是如何在 Golang + Ebiten 遊戲框架中完成「難度選擇」的。

🎯 問題脈絡:為什麼需要難度選擇?

如果遊戲只有單一難度,玩家會很快失去新鮮感。
難度選擇除了增加遊戲的重玩性之外,也讓遊戲能夠滿足不同層次的玩家需求。

  • 新手:希望快速上手,棋盤小一點、地雷少一點
  • 老手:挑戰更大的棋盤,思考更多策略
  • 速刷玩家:追求效率和最佳路線,需要固定難度

因此,我們需要一個直覺的方式,讓玩家能在遊戲開始前(或重來時)選擇不同難度。

🛠️ 解法思路

在 UI 設計上,有兩種常見作法:

下拉式選單 (Dropdown)

  • 類似網頁或應用程式的下拉選擇
  • 清楚直觀,但需要額外操作點擊

單一按鈕循環切換

  • 每次點擊按鈕,難度會在「初級 → 中級 → 高級」之間循環
  • 空間節省,不需額外 UI 元素
  • 可以加上 不同 icon 讓玩家快速辨識

在這裡我選擇第二種方式:一個按鈕,不同 icon 表示不同難度。
原因是踩地雷遊戲畫面已經很擠了(棋盤很大),UI 應該保持簡潔

💻 程式碼範例

以下是簡化後的範例:

紀錄難度選項


type Level int

const (
	Easy Level = iota
	Medium
	Hard
)

// 遊戲畫面狀態
type GameLayout struct {
	gameInstance *game.Game // 遊戲物件
	ClickCoord   *Coord     // 使用者點擊座標
	elapsedTime  int        // 經過時間
	Rows         int        // 紀錄遊戲 Row 大小
	Cols         int        // 紀錄遊戲 Col 大小
	MineCounts   int        // 紀錄 MineCounts
	ScreenHeight int
	ScreenWidth  int
	level        Level
}

更新 Level 邏輯

func (g *GameLayout) ChangeLevel() {
	g.level = (g.level + 1) % 3
}

重新繪製整個棋盤邏輯

// Restart - 重新建立 Game 狀態
func (g *GameLayout) Restart() {
	g.Rows = LevelSetupMap[g.level].Rows
	g.Cols = LevelSetupMap[g.level].Cols
	g.MineCounts = LevelSetupMap[g.level].MineCounts
	g.ScreenHeight = PanelHeight + gridSize*g.Rows
	g.ScreenWidth = gridSize * g.Cols
	ebiten.SetWindowSize(g.ScreenWidth, g.ScreenHeight)
	ebiten.SetWindowTitle(fmt.Sprintf("%s Mine Sweeper Grid", LevelMessage[g.level]))
	g.gameInstance = game.NewGame(g.Rows, g.Cols, g.MineCounts)
}

偵測變更邏輯

// 偵測 level icon 有被點擊
	if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
		xPos, yPos := ebiten.CursorPosition()
		if xPos >= ((g.ScreenWidth-1.5*gridSize)/2+buttonRectRelativePos.Min.X) &&
			xPos <= (g.ScreenWidth)/2+buttonRectRelativePos.Max.X+0.5*gridSize &&
			yPos >= buttonRectRelativePos.Min.Y &&
			yPos <= buttonRectRelativePos.Max.Y+3 {
			g.ChangeLevel()
			g.Restart()
		}
	}

這段程式碼的重點是:

  • Level 用 enum 形式定義(Easy, Medium, Hard)
  • icons 存放三個不同難度的圖片(例如小地雷、旗子、時鐘)
  • 每次玩家點擊按鈕,難度就會依序切換

執行結果

Easy
https://ithelp.ithome.com.tw/upload/images/20250901/20111580lYGBeF1Ke2.png
Medium
https://ithelp.ithome.com.tw/upload/images/20250901/20111580qOnVoZeulW.png
Hard
https://ithelp.ithome.com.tw/upload/images/20250901/20111580qhY7gWEDPw.png

🔍 視覺效果設計

  • 為了讓玩家一眼就能辨識當前難度,我設計了三種 icon:

  • Easy → 🌱 小嫩芽圖案(代表入門)

  • Medium → ⏳ 沙漏圖案(時間挑戰)

  • Hard → 💣 地雷圖案(最終 boss)

這樣一來,光看按鈕圖示就知道當前遊戲模式,省去了讀文字的時間。

✅ 結語

難度選擇雖然是個小功能,但卻大幅提升了遊戲的完整性。
這個設計同時兼顧了:

  • 玩家需求(不同程度的挑戰)
  • UI 簡潔(一個按鈕即可完成)
  • 程式結構清晰(enum + icon 映射)

明日預計

明天將要開始撰寫數獨遊戲規則與玩法理解的相關內容
講會說明數獨 9x9 規則、遊戲目標與勝負條件


上一篇
踩地雷遊戲:UI 顯示計時器
下一篇
Sudoku 遊戲:數獨遊戲規則與玩法理解
系列文
在 ai 時代 gopher 遊戲開發者的 30 天自我養成22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言